package com.liuyun.aes.liuyun;

import lombok.SneakyThrows;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;

/**
 * <p>
 * Copyright: (C), 2021/1/4 21:50
 * <p>
 * <p>
 * Company Information & Data Security Solutions Co., Ltd.
 *
 * @author LiuYun liujw@idss-cn.com
 * @version 1.0
 */
public class AesCBC {

    public static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
    public static final String KEY_ALGORITHM = "AES";

    /**
     * @param plaintext 明文字节数组
     * @param key       密钥字节数组
     * @param iv        IV字节数组
     * @return 密文字节数组
     */
    @SneakyThrows
    public static byte[] encrypt(byte[] plaintext, byte[] key, byte[] iv) {
        // 创建密码器
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        // 密钥
        SecretKeySpec keySpec = new SecretKeySpec(key, KEY_ALGORITHM);
        // IV矩阵（盐值）
        IvParameterSpec paramSpec = new IvParameterSpec(iv);
        // 初始化为加密模式的密码器
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, paramSpec);
        // 对明文加密
        return cipher.doFinal(plaintext);
    }

    /**
     * @param cipherText 密文字节数组
     * @param key        密钥字节数组
     * @param iv         IV字节数组
     * @return 明文文字节数组
     */
    @SneakyThrows
    public static byte[] decrypt(byte[] cipherText, byte[] key, byte[] iv) {
        // 创建密码器
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        // 密钥
        SecretKeySpec keySpec = new SecretKeySpec(key, KEY_ALGORITHM);
        // IV矩阵（盐值）
        IvParameterSpec paramSpec = new IvParameterSpec(iv);
        // 初始化为解密模式的密码器
        cipher.init(Cipher.DECRYPT_MODE, keySpec, paramSpec);
        return cipher.doFinal(cipherText);
    }

    /**
     * @param plaintext 明文字符串
     * @param key       密钥字符串
     * @param iv        IV字符串
     * @return base64密文字符串
     */
    public static String encrypt2Base64(String plaintext, byte[] key, byte[] iv) {
        byte[] encrypt = encrypt(string2Bytes(plaintext), key, iv);
        return base64Encode(encrypt);
    }

    /**
     * @param cipherText base64密文字符串
     * @param key        密钥字符串
     * @param iv         IV字符串
     * @return 明文字符串
     */
    public static String decrypt4Base64(String cipherText, byte[] key, byte[] iv) {
        byte[] decrypt = decrypt(base64Decode(cipherText), key, iv);
        return bytes2String(decrypt);
    }

    /**
     * @param plaintext 明文字符串
     * @param key       密钥字符串
     * @param iv        IV字符串
     * @return 密文字符串
     */
    @Deprecated
    private static String encrypt2(String plaintext, String key, String iv) {
        byte[] encrypt = encrypt(string2Bytes(plaintext), string2Bytes(key), string2Bytes(iv));
        return bytes2String(encrypt);
    }

    /**
     * @param cipherText 密文字符串
     * @param key        密钥字符串
     * @param iv         IV字符串
     * @return 明文字符串
     */
    @Deprecated
    private static String decrypt2(String cipherText, String key, String iv) {
        byte[] decrypt = decrypt(string2Bytes(cipherText), string2Bytes(key), string2Bytes(iv));
        return bytes2String(decrypt);
    }


    public static byte[] base64Decode(String base64) {
        return Base64.getDecoder().decode(base64);
    }


    public static String base64Encode(byte[] encryptByte) {
        return Base64.getEncoder().encodeToString(encryptByte);
    }

    /**
     * byte转换为string可能会丢失长度
     * 建议转换为base64
     * 【Java中String内置的public String(byte[] bytes)和public byte[] getBytes()对于不合法的utf-8字节流在解析时会增删字节。】
     *
     * @param data
     * @return
     */
    private static String bytes2String(byte[] data) {
        return new String(data, StandardCharsets.UTF_8);
    }

    public static byte[] string2Bytes(String data) {
        return data.getBytes(StandardCharsets.UTF_8);
    }

    /**
     * 生成密钥
     *
     * @param key 密钥字符串
     * @return
     */
    @SneakyThrows
    public static byte[] generateKey(String key) {
        // 创建AES的Key生产者
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
        secureRandom.setSeed(key.getBytes(StandardCharsets.UTF_8));
        // key作为随机数初始化出128位的key生产者: SecureRandom是生成安全随机数序列，key.getBytes()是种子，只要种子相同，序列就一样
        kgen.init(128, secureRandom);
        // 根据用户密码，生成一个密钥
        SecretKey secretKey = kgen.generateKey();
        // 返回基本编码格式的密钥，如果此密钥不支持编码，则返回null
        return secretKey.getEncoded();
    }

}
